/** Projecto de CPS | Python | C
 * gnpa.c
 *
 * GNPA - Gerador de Numeros Pseudo Aleatorios
 *
 * o programa pede ao utilizador o numero de 'N' a gerar
 *
 *
 * VirtualBox v4.1.8 com maquina virtual fornecida pelo professor
 *
 * CPU: Intel(R) Core(TM)2 Duo CPU  U7600  @  1.20GHz
 *
 * Registo de tempos em segundos
 *
 * N(10)      - 0.00
 * N(100)     - 0.00
 * N(1000)    - 0.01
 * N(5000)    - 0.03
 * N(10000)   - 0.09
 * N(50000)   - 0.37
 * N(100000)  - 0.72
 * N(500000)  - 3.95 
 * N(1000000) - 7.67 
 *
 *
 * Vitor Farropas | 12229 | vf@esdg.pt
 *
 * IPBEJA | ESTIG | MESI | 2011/2012 | 27.01.2012  
 *
 */
 
 #include <stdio.h>
 #include <math.h>
 
 #include <stdlib.h>
 #include <time.h>
 #include <string.h>
 
 #define OK         1
 #define NOK		0
 
/** variavel global: tabela 'T[1000]' elementos */
int T[1000];
  
/** 
 * inicio das funcoes auxiliares 
 */

/** 
 * inicializa o clock para os numeros aleatorios: rand() 
 * primeira funcao a ser chamada para iniciar o gerador aleatorio 
 */
void initrand(void) 
{ 
    srand((unsigned int)(time(NULL))); 
} 

/** funcao que gera o 'p' de continuacao */
int geraPc(int z1, int z2, int z3)
{ 
    return (z1 * 100) + (z2 * 10) + (z3 * 1);
}


/** preenche a tabela 'T' com valores aleatorio entre 0..9 */
void preencher_tabela(void)
{
	int x;
	int ct;
	
	/**
	 * tabela para controlo de contagem da quantidade de algarismos
	 * que ja foram gerados de 0..9
	 */
	int tab_contagem[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
	
	/** inicializa a tabela 'T' com zeros*/
    for(x = 0; x < 1000; x++)
	{
		T[x] = 0;
	}
	
	x = 0;
    ct = 0;

    while (ct < 1000)
	{
        /** gera um numero 0..9 */
        x = rand() % 10;
        
        /** verifica se ja tem os 100 elementos desse numero */
        if (tab_contagem[x] > 99)
        {
        	/** se ja tem 100 elementos de 'x' -> continua... */
            continue;
  	
        }
        
        /** conta elementos 'x' de 0..9, guarda na tabela 'T' */
        tab_contagem[x] += 1;
        T[ct] = x;
        ct += 1;
    }
}

/**
 * conta a quantidade de algarismos 0..9 da tabela T. Devem estar 100 de cada
 * esta funcao podera nao ter razao de ser por si so... 
 */ 
void contagem(void)
{
	int x;
	int tab_contagem[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
	
    for(x = 0; x < 1000; x++)
	{
		switch(T[x])
		{
			case 0: tab_contagem[0] += 1;
			break;
			case 1: tab_contagem[1] += 1;
			break;
			case 2: tab_contagem[2] += 1;
			break;
			case 3: tab_contagem[3] += 1;
			break;
			case 4: tab_contagem[4] += 1;
			break;
			case 5: tab_contagem[5] += 1;
			break;
			case 6: tab_contagem[6] += 1;
			break;
			case 7: tab_contagem[7] += 1;
			break;
			case 8: tab_contagem[8] += 1;
			break;
			case 9: tab_contagem[9] += 1;
			break;			
			default: printf("......ERRO: contagem(switch)");
			break;
		}
    }
    printf("\n...: contagem(): tab_contagem[0] = %d", tab_contagem[0]);
    printf("\n...: contagem(): tab_contagem[1] = %d", tab_contagem[1]);
    printf("\n...: contagem(): tab_contagem[2] = %d", tab_contagem[2]);
    printf("\n...: contagem(): tab_contagem[3] = %d", tab_contagem[3]);
    printf("\n...: contagem(): tab_contagem[4] = %d", tab_contagem[4]);
    printf("\n...: contagem(): tab_contagem[5] = %d", tab_contagem[5]);
    printf("\n...: contagem(): tab_contagem[6] = %d", tab_contagem[6]);
    printf("\n...: contagem(): tab_contagem[7] = %d", tab_contagem[7]);
    printf("\n...: contagem(): tab_contagem[8] = %d", tab_contagem[8]);
    printf("\n...: contagem(): tab_contagem[9] = %d", tab_contagem[9]);
}

/** faz a permuta de T[l]<->T[0] */
void permuta(int l)
{
	int tmp;
	
    tmp = T[l];
    T[l] = T[0];
    T[0] = tmp;
}
	
/** fim das funcoes auxiliares */
 
 
/*
 * funcao main()
 */
int main(int argc, char* argv[])
 {
    int i;
    
    int N; //numero pseudo aleatorio gerado!
    int z1, z2, z3, z4; // z's para calculo de 'N'
    int p; // indice do valor de 'T' a usar
    int l; // indica de referencia para a permuta()
    int qN; // quantidade de numeros a gerar
    
	clock_t ti; // regista o tempo inicial
	clock_t tf; // regista o tempo final

	double te; // tempo de execucao
		
	/** 
	 * pergunta ao utilizador a quantidade de numeros 'qN'
	 * pseuso aleatorio a gerar
	 */
	printf("\nQuantos numeros quer gerar? ");
	scanf("%d", &qN);
	
	/** contagem de tempo - INICIO * ti */
    ti = clock();

	/** inicializa o clock para numeros aleatorios */
	initrand();

	/** preenche a tabela 'T' com valores aleatorio 0..9 */
	preencher_tabela();
	
	/** verifica que a tabela 'T' tem 100 elementos de cada 0..9 */
	//contagem();
	
	/** gera indice 'p' inicial entre 0..999 */
	p = rand() % 1000;

	/**  */
	for(i = 0; i < qN; i++)
	{
	    if (p < 1000)
	    {
	        l = p;
	        z1 = T[l];
	    }
	    else
	    {
	        l = 0;
	        z1 = T[l];
	    }
	    permuta(l);
	    
	    if(p < 999)
	    {
	        l = p + 1;
	        z2 = T[l];
	    }
	    else
	    {
	        l = 0;
	        z2 = T[l];
	    }
	    permuta(l);
	
	    if(p < 998)
	    {
	        l = p + 2;
	        z3 = T[l];
	    }
	    else
	    {
	        l = 0;
	        z3 = T[l];
	    }
	    permuta(l);
	
	    if(p < 997)
	    {
	        l = p + 3;
	        z4 = T[l];
	    }
	    else
	    {
	        l = 0;
	        z4 = T[l];
	    }
	    permuta(l);
	   
		/** numero pseudo aleatorio gerado! */
	    N = z1 << 12 | z2 << 8 | z3 << 4 | z4;
	
	    /** mostra o numero 'N' pseudo aleatorio gerado */
	    printf("N(%d) - %d\n",i+1,N);
	
	    /** gera indice 'p' de continuacao com base nos Zs saidos... */
	    p = geraPc(z1, z2, z3);
	    
	    /** 
	     * possivel solucao para a nao repeticao de numeros gerados!
	     * passa por inicializar o clock para rand(), e gerar um novo
	     * valor para o indice 'p'
	     */
	    //initrand();
	    //p = rand() % 1000;
	    
	} // fim for()
	
	/** contagem de tempo - FIM * tf */	
	tf = clock();
	
	/** calculo do tempo de execucao do programa 
	 * te = (tf - ti) / (double) CLOCKS_PER_SEC -> tempo em segundos
	 */
	te = (tf - ti) / (double) CLOCKS_PER_SEC;

	printf("Tempo de execucao: %.5lf s\n", te);
	
	return OK;
 }
